﻿namespace WebPacker.Packs.Resources
{
	using System;
	using System.IO;
	using System.Net;

	public sealed class RemoteResource : IResource
	{
		private readonly Uri uri;
		private string content;
		private DateTime lastModifiedTime;
		private string etag;

		public RemoteResource(string uriString)
		{
			if (uriString == null)
			{
				throw new ArgumentNullException("uriString");
			}

			if (!Uri.TryCreate(uriString, UriKind.Absolute, out uri))
			{
				throw new ArgumentException("Uri \"{0}\" is invalid.");
			}

			Name = uriString;
			lastModifiedTime = DateTime.MinValue;
		}

		public string Name
		{
			get;
			private set;
		}

		public bool EnableMonitoring { get; set; }

		public string GetContent()
		{
			Update();
			return content;
		}

		public bool Update()
		{
			return Update(content != null);
		}

		public DateTime GetLastModifiedTime()
		{
			if (lastModifiedTime == DateTime.MinValue)
			{
				Update();
			}

			return lastModifiedTime;
		}

		private bool Update(bool checkState)
		{
			var request = HttpWebRequest.Create(uri);
			var httpRequest = request as HttpWebRequest;

			if (httpRequest != null)
			{
				httpRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
				httpRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

				if (checkState)
				{
					httpRequest.IfModifiedSince = (DateTime)lastModifiedTime;

					if (etag != null)
					{
						httpRequest.Headers.Add(HttpRequestHeader.IfNoneMatch, etag);
					}
				}
			}

			try
			{
				using (var response = request.GetResponse())
				using (var stream = response.GetResponseStream())
				using (var reader = new StreamReader(stream))
				{
					string newEtag = null;
					var newLastModifiedTime = DateTime.Now;

					var httpResponse = response as HttpWebResponse;

					if (httpResponse != null)
					{
						newEtag = httpResponse.Headers[HttpResponseHeader.ETag];
						newLastModifiedTime = httpResponse.LastModified;
					}

					var newContent = reader.ReadToEnd();

					if (checkState && content == newContent)
					{
						return false;
					}

					content = newContent ?? string.Empty;
					lastModifiedTime = newLastModifiedTime;
					etag = newEtag;
				}
			}
			catch (WebException e)
			{
				if (e.Response is HttpWebResponse)
				{
					if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotModified)
					{
						return false;
					}
				}

				throw;
			}

			return true;
		}
	}
}